/*
 * abstract class for templating ships
 */

package item.ship;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Random;

import item.Item;
import item.defensive.Armor;
import item.defensive.Crew;
import item.defensive.DefensiveEnhancement;
import item.defensive.Hull;
import item.defensive.Shield;
import item.drone.Drone;
import item.engine.Engine;
import item.mod.Mod;
import item.weapon.DamageType;
import item.weapon.Weapon;

public abstract class Ship extends Item implements Serializable {
	
	private static final long serialVersionUID = -718516041231267159L;
	
	public static int id = 1;
	private int idNum;
	protected int amr; //armor
	protected int maxAMR; //maximum armor
	protected int hll; //hull
	protected int maxHLL; //maximum hull
	protected int sld; //shield
	protected int maxSLD; //maximum shield
	protected int crw; //crew
	protected int maxCRW; //maximum crew
	protected ArrayList<Weapon> weapon; //weapons on the ship
	protected ArrayList<DefensiveEnhancement> defensive; //shields on the ship
	protected ArrayList<Engine> engine; //engines on the ship
	protected ArrayList<Drone> drone; //drones on the ship
	private int maxWep; //maximum number of weapons
	private int maxDef; //maximum number of defensive enhancements
	private int maxEng; //maximum number of engines
	private int maxDrn; //maximum number of drones
	private ArrayList<Mod> mod; //mods on the ship;
	private int maxMod; //maximum number of modifications;
	private Stance stance; //stance of the ship
	private int actionPoints; //current number of action points
	private int maxActionPoints; //the most action points
	
	//creates a Ship object
	
	public Ship(int cost, int condition, int level, int weight, 
			int amr, int maxAMR, int hll, int maxHLL, int crw, int maxCRW) {
		

		super(cost, condition, level, weight);
		//defensive value totals
		//shields need installation
		
		this.amr = amr;
		this.maxAMR = maxAMR;
		this.hll = hll;
		this.maxHLL = maxHLL;
		this.crw = crw;
		this.maxCRW = maxCRW;
		
		//defaults item ArrayLists to empty ArrayLists
		
		weapon = new ArrayList<Weapon>();
		defensive = new ArrayList<DefensiveEnhancement>();
		engine = new ArrayList<Engine>();
		drone = new ArrayList<Drone>();
		
		//defaults the maximum slots to zero
		
		maxWep = 0;
		maxDef = 0;
		maxEng = 0;
		maxDrn = 0;
		maxMod = 0;
		setStance(Stance.NORMAL);
		
		maxActionPoints = 5;
		
		
		name = id + "";
		spritePath += "ship\\";
		name = id +"";
		idNum = id;
		id++;
	}//end of constructor with parameters Ship
	
	public boolean equals(Object o) {
		if(!(o instanceof Ship))
			return false;
		return ((Ship) o).idNum == this.idNum;
	}
	
	public int getID() {
		return idNum;
	}
	
	public void regen () {
		for(DefensiveEnhancement d : defensive) {
			if(d instanceof Shield)
				sld += ((Shield) d).shieldRegen;
		}
	}
	
	//lets the user choose a weapon to fire on enemy ships
	//returns true if it fires, false if it doesn't
	
	public boolean attack(Weapon w, Ship target) {
		
		if(w.actionPoint > actionPoints)
			return false;
		
		
		Random r = new Random(); //for calculating hit count
		
		for(int i = 0; i < w.numAttacks; i++) {
			
			if(r.nextInt(100) < (w.hitRate * 1.0 * this.speed() / (target.speed() * 1.0))) {
				
				int damage = 0; //amount of damage the shot will deal
				int dmgFactor = (int) (w.damage * (w.condition / 100.0)
						* (this.condition / 100.0)
						* (this.crw / (this.maxCRW / 2.0))); //damage the shot does after multipliers
				
				switch(r.nextInt(50)) {
					case 49: //critical shot
						damage += dmgFactor * 2;
						break;
						
					case 48: //glancing shot
						damage += dmgFactor / 2;
						break;
						
					default: //standard shot
						damage += dmgFactor;
				}//calculates damage
				damage *= stance.offensiveMult;
				
				for(Mod m : mod) {
					damage *= m.getDamageMult();
					switch(w.type) {
						case ARMOR_PENETRATING:
							damage *= m.getArmorMult();
							break;
						case PLASMA:
							damage *= m.getPlasmaMult();
							break;
						case LASER:
							damage *= m.getLaserMult();
							break;
						case MISSILE:
							damage *= m.getMissileMult();
							break;
						default:
							break;
					}//specific multipliers
				}//applies modification multipliers
				
				double armor = 1.0; // total armor multiplier
				double defensive = 1.0; //total defensive multiplier
				double crew = 1.0; //total crew multiplier
				double hull = 1.0; //total hull multiplier
				double shield = 1.0; //total shield multiplier
				
				for(Mod m: mod) {
					armor *= m.getArmorMult();
					defensive *= m.getDefensiveMult();
					shield *= m.getShieldMult();
					crew *= m.getCrewMult();
					hull *= m.getHullMult();
				}//applies defensive modifications
				
				
				damage = (int) (damage / defensive);
				
				
				if(target.getShields() == 0 || w.type == DamageType.MISSILE) {
					target.setArmor(target.getArmor() - (int) (damage * w.type.armorMultiplier / armor * .4));
					
					//armor blocks some hull and crew damage

					if(target.getArmor() < 0) {
						target.setArmor(0);
					}
						target.setHull(target.getHull() - (int) (damage * w.type.hullMultiplier / hull * .4
								* (1.0 - (target.getArmor() / (1.0 * target.getMaxArmor())))));
						if(target.getHull() < 0)
							target.setHull(0);
						
						
						target.setCrew(target.getCrew() - (int) (damage * w.type.crewMultiplier / crew * .2
								* (1.0 - (target.getArmor() / (1.0 * target.getMaxArmor())))));
						if(target.getCrew() < 0)
							target.setCrew(0);
				}//shields are down / bypassed
				else {
					target.setShields(target.getShields() - (int) (damage * w.type.shieldMultiplier * shield));
					if(target.getShields() < 0)
						target.setShields(0);
				}//weapon hits the shield

				
			}//calculates if a shot hit
			
		}//loops until the weapon finishes firing
		
		actionPoints -= w.actionPoint;
		
		return true;
		
	}//end of method attack

	//returns dying message as well as applies any death effects.
	
	public abstract String die();

	//returns the speed of the engines
	
	public int speed() {
		int speed = 0;
		for(Engine e: engine)
			speed += (int) (e.speed * (e.condition / 100.0));
		speed -= this.weight;
		return speed;
	}//end of method speed
	
	//returns the total weight
	
	public int weight() {
		int weight = this.weight;
		for(Item i : weapon)
			weight += i.weight;
		for(DefensiveEnhancement i : defensive)
			weight += ((Item) i).weight;
		for(Item i : engine)
			weight += i.weight;
		for(Item i : drone)
			weight += i.weight;
		return weight;
	}//end of method weight
	
	//adds a weapon if there is room
	
	public void equip(Weapon weapon) {
		if(this.weapon.size() <= maxWep)	 {
			this.weapon.add(weapon);
			this.weight += weapon.weight;
		}
	}//end of method equip	

	//adds a defensive enhancement if there is room
	
	public void equip(DefensiveEnhancement def) {
		if(this.defensive.size() <= maxDef) {
			this.defensive.add(def);
			if(def instanceof Shield) {
				this.maxSLD += ((Shield) def).shield;
				this.sld += ((Shield) def).shield;
			}
			if(def instanceof Armor) {
				this.maxAMR += ((Armor) def).armor;
				this.amr += ((Armor) def).armor;
			}
			if(def instanceof Hull) {
				this.maxHLL += ((Hull) def).hull;
				this.hll += ((Hull) def).hull;
			}
			if(def instanceof Crew) {
				this.maxCRW += ((Crew) def).crew;
				this.crw += ((Crew) def).crew;
			}
			this.weight += ((Item) def).weight;
		}
	}//end of method equip
	
	//adds an engine if there is room
	
	public void equip(Engine engine) {
		if(this.engine.size() <= maxEng) {
			this.engine.add(engine);
			this.weight += engine.weight;
		}
	}//end of method equip
	
	//adds a drone if it room
	
	public void equip(Drone drone) {
		if(this.drone.size() <= maxDrn) {
			this.drone.add(drone);
			this.weight += drone.weight;
		}
	}//end of method equip
	
	//equips a mod if there is room
	
	public void equip(Mod mod) {
		if(this.mod.size() <= maxMod) {
			this.mod.add(mod);
			this.weight += mod.weight;
		}
	}//end of method equip
	
	//removes a weapon if it exists
	
	public void dequip(Weapon weapon) {
		if(this.weapon.contains(weapon)) {
			this.weapon.remove(weapon);
			this.weight -= weapon.weight;
		}
	}//end of method dequip
	
	//removes a defensive enhancement if it exists

	public void dequip(DefensiveEnhancement def) {
		if(this.defensive.contains(def)) {
			this.defensive.remove(def);
			if(def instanceof Shield)
				this.maxSLD -= ((Shield) def).shield;
			if(def instanceof Armor)
				this.maxAMR -= ((Armor) def).armor;
			if(def instanceof Hull)
				this.maxHLL -= ((Hull) def).hull;
			if(def instanceof Crew)
				this.maxCRW -= ((Crew) def).crew;
			this.weight -= ((Item) def).weight;
		}
	}//end of method dequip
	
	//removes a engine if it exists

	public void dequip(Engine engine) {
		if(this.engine.contains(engine)) {
			this.engine.remove(engine);
			this.weight -= engine.weight;
		}
	}//end of method dequip
	
	//removes a defensive enhancement if it exists

	public void dequip(Drone drone) {
		if(this.drone.contains(drone)) {
			this.drone.remove(drone);
			this.weight -= drone.weight;
		}
	}//end of method dequip
	
	//removes a mod if it exists
	
	public void dequip(Mod mod) {
		if(this.mod.contains(mod)) {
			this.mod.remove(mod);
			this.weight -= mod.weight;
		}
	}//end of method dequip
	
	//gets the name of the ship
	
	public String toString() {
		return name;
	}//end of method toString

	
	//returns the name of the ship
	public String getName(){
		return name;
	}//getName
	
	
	//sets the name of the ship
	public void setName(String n){
		
		name = n;
	}//setName
	
	
	//gets the current armor value of the ship

	public int getArmor() {
		return amr;
	}//end of getter getArmor

	//sets the current armor value of the ship
	
	public void setArmor(int amr) {
		this.amr = amr;
	}//end of setter setArmor

	//gets the current shield value of the ship
	
	public int getShields() {
		return sld;
	}//end of getter getShields
	
	//sets the current shield value of the ship

	public void setShields(int sld) {
		this.sld = sld;
	}//end of setter setShields

	//gets the current crew value of the ship
	
	public int getCrew() {
		return crw;
	}//end of getter getCrew

	
	//sets the current crew value of the ship
	
	public void setCrew(int crw) {
		this.crw = crw;
	}//end of setter setCrew

	//gets an array of weapons on the ship
	
	public ArrayList<Weapon> getWeapon() {
		return weapon;
	}//end of getter getWeapon
	
	//sets an array of weapons installed on the ship

	public void setWeapon(ArrayList<Weapon> weapon) {
		this.weapon = weapon;
	}//end of setter setWeapon
	
	//gets an array of defensive enhancements installed on the ship
	
	public ArrayList<DefensiveEnhancement> getDefensiveEnhancements() {
		return defensive;
	}//returns the defensive enhancements installed on the sip

	//sets an array of the defensive enhancements installed 
	
	public void setDefensiveEnhancements(ArrayList<DefensiveEnhancement> defensive) {
		this.defensive = defensive;
	}//end of setter setDefensiveEnhancements
	
	//gets the maximum armor value

	public int getMaxArmor() {
		return maxAMR;
	}//end of getter getMaxArmor
	
	//sets the maximum armor value

	public void setMaxArmor(int maxAMR) {
		this.maxAMR = maxAMR;
	}//end of setter setMaxArmor
	
	//get the maximum shield value

	public int getMaxShields() {
		return maxSLD;
	}//end of getter getMaxShield
	
	//sets the maximum shield value

	public void setMaxShields(int maxSLD) {
		this.maxSLD = maxSLD;
	}//end of setter setMaxShield

	//gets the maximum crew value
	
	public int getMaxCrew() {
		return maxCRW;
	}//end of getter getMaxCrew
	
	//sets the maximum crew value

	public void setMaxCrew(int maxCRW) {
		this.maxCRW = maxCRW;
	}//end of setter setMaxCrew
	
	//gets the engines

	public ArrayList<Engine> getEngine() {
		return engine;
	}//end of getter getEngine

	//sets the engines
	
	public void setEngine(ArrayList<Engine> engine) {
		this.engine = engine;
	}//end of setter setEngine

	//gets the drones
	
	public ArrayList<Drone> getDrone() {
		return drone;
	}//end of getter getDrone
	
	//sets the drones

	public void setDrone(ArrayList<Drone> drone) {
		this.drone = drone;
	}//end of setter setDrone

	//gets the maximum number of drones
	
	public int getMaxDrone() {
		return maxDrn;
	}//end of getter getMaxDrone
	
	//sets the maximum number of drones

	public void setMaxDrone(int maxDrn) {
		this.maxDrn = maxDrn;
	}//end of setter setMaxDrone

	//gets the maximum number of engines
	
	public int getMaxEngine() {
		return maxEng;
	}//end of getter getMaxEngine
	
	//sets the maximum number of engines

	public void setMaxEngine(int maxEng) {
		this.maxEng = maxEng;
	}//end of setter setMaxEngine

	//gets the maximum number of defensive enhancements
	
	public int getMaxDefensive() {
		return maxDef;
	}///end of getter getMaxDefensive

	//sets the maximum number of defensive enhancements
	
	public void setMaxDefensive(int maxDef) {
		this.maxDef = maxDef;
	}//end of setter setMaxDefensive
	
	//gets the maximum number of weapons

	public int getMaxWeapon() {
		return maxWep;
	}//end of getter getMaxWeapon
	
	//sets the maximum number of weapons

	public void setMaxWeapon(int maxWep) {
		this.maxWep = maxWep;
	}//end of setter setMaxWeapon
	
	//gets the hull integrity
	
	public int getHull() {
		return hll;
	}//end of getter getHull
	
	//sets the hull integrity
	
	public void setHull(int hll) {
		this.hll = hll;
	}//end of setter setHull
	
	//gets the maximum hull value
	
	public int getMaxHull() {
		return maxHLL;
	}//end of getter getMaxHull
	
	//sets the maximum hull value
	
	public void setMaxHull(int maxHLL) {
		this.maxHLL = maxHLL;
	}//end of setter setMaxHull

	//gets the maximum number of modifications
	
	public int getMaxMod() {
		return maxMod;
	}//end of getter getMaxMod

	//sets the maximum number of modifications
	
	public void setMaxMod(int maxMod) {
		this.maxMod = maxMod;
	}//end of setter setMaxMod

	//returns the current stance
	
	public Stance getStance() {
		return stance;
	}//end of getter getStance

	//sets the stance to a new one
	
	public void setStance(Stance stance) {
		this.stance = stance;
	}//end of setter setStance
	
	//gets the mods installed on the ship
	
	public ArrayList<Mod> getMod() {
		return mod;
	}//end of getter getMod
	
	//sets the mods installed on the ship
	
	public void setMod(ArrayList<Mod> mod) {
		this.mod = mod;
	}//end of method setMod
	
	//gets the maximum action points

	public int getMaxActionPoints() {
		return maxActionPoints;
	}//end of getter getMaxActionPoints

	//sets the maximum action points the ship has a start of battle
	
	public void setMaxActionPoints(int maxActionPoints) {
		this.maxActionPoints = maxActionPoints;
	}//end of setter setMaxActionPoints
	
	//gets the number of action points the ship has

	public int getActionPoints() {
		return actionPoints;
	}//end of method getActionPoints

	//sets the number of action points the ship has
	
	public void setActionPoints(int actionPoints) {
		this.actionPoints = actionPoints;
	}//end of setter setActionPoints
	
	
	//returns the ship class
	public abstract String getShipClass();
	
	//returns a comparator for ships based on speed
	
	public static Comparator<Ship> getSpeedComparator() {
		return new Comparator<Ship>() {

			//compares two ships
			
			public int compare(Ship arg0, Ship arg1) {
				return arg1.speed() - arg0.speed();
			}//end of method compare
			
		};
	}//end of method getSpeedComparator
	
	
	public boolean tempEquals(Ship s){
	

		return s.name == this.name;
	}
	
}//end of class Ship
